import json
import os
import re
from urllib.parse import urlsplit
from typing import Dict, Any, Optional

from jinja2 import Environment
from pyassured.core.project_path import config
from pyassured.core.logger import Log
from pyassured.core.emoji_utils import EmojiUtils

log = Log()


class Har2ApiUtil:
    """
    HAR文件转换为API文件的工具类
    
    将HAR（HTTP Archive）文件转换为符合项目规范的Python API类文件，
    支持自动生成带有@Desc装饰器的API类。
    """

    @staticmethod
    def har2api(har_file_path: str, output_dir: str = None, class_prefix: str = "", class_suffix: str = "Api") -> bool:
        """
        将HAR文件转换为Python API文件
        
        Args:
            har_file_path: HAR文件的完整路径
            output_dir: 输出目录，如果为None则使用config.api_dir
            class_prefix: 生成的类名前缀
            class_suffix: 生成的类名后缀（默认为Api）
            
        Returns:
            bool: 转换是否成功
            
        Raises:
            FileNotFoundError: 当HAR文件不存在时
            json.JSONDecodeError: 当HAR文件格式错误时
            Exception: 其他转换过程中的错误
        """
        try:
            log.info(f"{EmojiUtils.INFO} 开始转换HAR文件: {har_file_path}")

            # 检查HAR文件是否存在
            if not os.path.exists(har_file_path):
                raise FileNotFoundError(f"HAR文件不存在: {har_file_path}")

            # 确定输出目录
            if output_dir is None:
                output_dir = config.api_dir

            # 确保输出目录存在
            os.makedirs(output_dir, exist_ok=True)

            env = Environment()
            env.filters["remove_key"] = remove_key

            # 优化的模板，符合项目规范
            template_str = """{%- for dict_item in parent_dict -%}
{% if loop.index == 1 %}from pyassured.core.base_api import BaseApi, Desc

{% endif %}
@Desc("{{ dict_item.get('description', dict_item.get('filename')) }}")
class {{ dict_item.get('filename') }}(BaseApi):
{%- set new_dict = dict_item|remove_key("filename")|remove_key("description") %}
{%- for key, value in new_dict.items() %}
    {{ key }} = {{ value }}
{%- endfor %}

{% endfor %}"""

            template = env.from_string(template_str)

            result = []
            class_names_used = set()  # 用于跟踪已使用的类名

            log.info(f"{EmojiUtils.INFO} 读取HAR文件: {har_file_path}")

            with open(har_file_path, 'r', encoding="utf-8-sig") as f:
                har_content = f.read()

            load_dict = json.loads(har_content)
            entries = load_dict.get('log', {}).get('entries', [])

            if not entries:
                log.warning(f"{EmojiUtils.WARNING} HAR文件中没有找到请求记录")
                return

            log.info(f"{EmojiUtils.INFO} 找到 {len(entries)} 个请求记录")

            for entry in entries:
                api_data = Har2ApiUtil._parse_har_entry(entry, class_prefix, class_suffix)
                if api_data and api_data.get('method') != '"OPTIONS"':
                    # 处理重复的类名
                    original_name = api_data['filename']
                    counter = 1
                    while api_data['filename'] in class_names_used:
                        api_data['filename'] = f"{original_name}{counter}"
                        counter += 1
                    class_names_used.add(api_data['filename'])
                    result.append(api_data)

            if not result:
                log.warning(f"{EmojiUtils.WARNING} 没有有效的API请求可以转换")
                return False

            # 渲染模板
            file_content = template.render(parent_dict=result)
            har_file_name = os.path.basename(har_file_path)
            output_path = os.path.join(output_dir, har_file_name.replace('.har', '_api.py'))

            with open(output_path, "w", encoding="utf-8") as f:
                f.write(file_content)

            log.success(f"{EmojiUtils.SUCCESS} 成功生成API文件: {output_path}")
            log.info(f"{EmojiUtils.INFO} 共转换 {len(result)} 个API接口")
            return True

        except FileNotFoundError as e:
            log.error(f"{EmojiUtils.ERROR} {e}")
            return False
        except json.JSONDecodeError as e:
            log.error(f"{EmojiUtils.ERROR} HAR文件格式错误: {e}")
            return False
        except Exception as e:
            log.error(f"{EmojiUtils.ERROR} 转换过程中发生错误: {e}")
            return False

    @staticmethod
    def _parse_har_entry(entry: Dict[str, Any], class_prefix: str = "", class_suffix: str = "Api") -> Optional[
        Dict[str, Any]]:
        """
        解析单个HAR条目
        
        Args:
            entry: HAR文件中的单个请求条目
            class_prefix: 生成的类名前缀
            class_suffix: 生成的类名后缀
            
        Returns:
            Dict[str, Any]: 解析后的API数据，如果解析失败返回None
        """
        try:
            request = entry.get('request', {})
            if not request:
                return None

            url = request.get('url', '')
            if not url:
                return None

            parsed_url = urlsplit(url)
            method = request.get('method', 'GET')

            api_data = {
                'path': f'"{parsed_url.path}"',
                'method': f'"{method}"'
            }

            # 处理请求头
            headers = request.get('headers', [])
            if headers:
                headers_dict = {header['name']: header['value'] for header in headers if isinstance(header, dict)}
                # 过滤掉不必要的头信息，只保留业务相关的头
                important_headers = ['authorization', 'content-type', 'accept', 'csrf-token', 'x-auth-token',
                                     'organization', 'project', 'x-api-key', 'x-requested-with']
                filtered_headers = {k: v for k, v in headers_dict.items()
                                    if k.lower() in important_headers or (
                                                k.lower().startswith('x-') and not k.lower().startswith(':'))}
                if filtered_headers:
                    api_data['headers'] = str(filtered_headers)

            # 处理请求体数据（POST、PUT、DELETE等非GET方法）
            if method != 'GET' and 'postData' in request:
                post_data = request['postData']
                mime_type = post_data.get('mimeType', '')

                if mime_type.startswith('application/json') and 'text' in post_data:
                    try:
                        json_data = json.loads(post_data['text'])
                        # 将JSON数据格式化为Python字典格式
                        api_data['json'] = repr(json_data)
                    except json.JSONDecodeError:
                        log.warning(f"{EmojiUtils.WARNING} 无法解析JSON数据，将作为原始JSON字符串处理")
                        # 如果无法解析为JSON，仍然使用json字段但作为字符串
                        api_data['json'] = f'"""{post_data["text"]}"""'
                elif mime_type.startswith('application/x-www-form-urlencoded') and 'params' in post_data:
                    form_data = {param['name']: param['value'] for param in post_data['params']}
                    api_data['data'] = str(form_data)
                elif 'text' in post_data and post_data['text'].strip():
                    # 处理其他类型的POST数据（如原始文本、XML等）
                    api_data['data'] = f'"""{post_data["text"]}"""'

            # 处理查询参数
            query_string = request.get('queryString', [])
            if query_string:
                query_dict = {param['name']: param['value'] for param in query_string}
                api_data['params'] = str(query_dict)

            # 生成类名和描述
            path_parts = [part for part in parsed_url.path.split('/') if part]
            if path_parts:
                class_name = Har2ApiUtil._path_to_class_name(path_parts[-1] or 'api', class_prefix, class_suffix)
            else:
                class_name = Har2ApiUtil._path_to_class_name('ApiRequest', class_prefix, class_suffix)

            api_data['filename'] = class_name
            api_data['description'] = f"{method} {parsed_url.path}"

            return api_data

        except Exception as e:
            log.warning(f"{EmojiUtils.WARNING} 解析HAR条目失败: {e}")
            return None

    @staticmethod
    def _path_to_class_name(path: str, class_prefix: str = "", class_suffix: str = "Api") -> str:
        """
        将URL路径转换为类名
        
        Args:
            path: URL路径部分
            class_prefix: 类名前缀
            class_suffix: 类名后缀
            
        Returns:
            str: 转换后的类名
        """
        # 移除特殊字符，只保留字母数字和下划线
        clean_path = re.sub(r'[^a-zA-Z0-9_]', '_', path)
        # 转换为驼峰命名
        parts = clean_path.split('_')
        base_name = ''.join(part.capitalize() for part in parts if part)

        # 确保基础名称以字母开头
        if not base_name or not base_name[0].isalpha():
            base_name = 'Api' + base_name

        # 构建完整的类名：前缀 + 基础名称 + 后缀
        class_name = class_prefix + base_name + class_suffix

        return class_name

    @staticmethod
    def __name_convert_to_camel(name: str) -> str:
        """
        下划线转驼峰（保留向后兼容性）
        
        Args:
            name: 需要转换的字符串
            
        Returns:
            str: 转换后的驼峰命名字符串
        """
        contents = re.findall('_[a-z]+', name)
        for content in set(contents):
            name = name.replace(content, content[1:].title())
        return "".join(name[:1].upper() + name[1:])


def remove_key(d: Dict[str, Any], key: str) -> Dict[str, Any]:
    """
    从字典中移除指定的键（Jinja2过滤器函数）
    
    Args:
        d: 源字典
        key: 要移除的键
        
    Returns:
        Dict[str, Any]: 移除指定键后的新字典
    """
    new_dict = d.copy()
    new_dict.pop(key, None)  # 使用pop的默认值参数，避免KeyError
    return new_dict


if __name__ == '__main__':
    try:
        har_file = "login.har"  # 修改为实际的HAR文件名
        Har2ApiUtil.har2api(har_file)
        print(f"✅ 成功转换HAR文件: {har_file}")
    except Exception as e:
        print(f"❌ 转换失败: {e}")
